package se.webbzon.boltzmann.editor.propertyframe;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.ArrayDeque;
import java.util.Collection;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.WindowConstants;

import se.webbzon.boltzmann.editor.mvc.EditorModel;
import se.webbzon.oschi01.worldeditor.DeflatableWorldObject;
import se.webbzon.oschi01.worldobject.WorldObject;

public abstract class WorldObjectPropertyFrame {
	
	// The frame associated with this property frame
	private final JInternalFrame frame;
	
	// The mvc-model of the editor
	private final EditorModel model;
	
	// The world object associated with this property frame
	private final WorldObject instance;
	
	// The tab pane associated with this property frame
	private final JTabbedPane tabPane;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public WorldObjectPropertyFrame(EditorModel model, WorldObject instance) {
		this.model = model;
		this.instance = instance;
		if (instance instanceof DeflatableWorldObject)
			frame = new JInternalFrame(	"Properties for " + 
								((DeflatableWorldObject) instance).getName());
		else
			frame = new JInternalFrame();
		frame.setMaximizable(false);
		frame.setClosable(true);
		frame.setResizable(true);
		frame.setLayout(new BorderLayout());
		tabPane = new JTabbedPane();
		frame.add(tabPane,BorderLayout.CENTER);
		
		JPanel lowerPanel = new JPanel();
		lowerPanel.setLayout(new BorderLayout());
		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.X_AXIS));
		buttonPanel.add(new JButton(new OKAction()),BorderLayout.WEST);
		buttonPanel.add(Box.createRigidArea(new Dimension(8,0)));
		buttonPanel.add(new JButton(new CancelAction()),BorderLayout.CENTER);
		buttonPanel.add(Box.createRigidArea(new Dimension(8,0)));
		buttonPanel.add(new JButton(new ApplyAction()),BorderLayout.EAST);
		lowerPanel.add(buttonPanel,BorderLayout.EAST);
		frame.add(lowerPanel,BorderLayout.SOUTH);
		
		frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		model.getDesktopPane().add(frame);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Initializes the property frame. This method must be called in the
	 * end of the constructor. **/
	public void initialize() {
		frame.pack();
		frame.setVisible(true);
		frame.toFront();
	}
	
	/** This method is invoked when the user pressed the apply button
	 * or the OK button. Is the apply method was successful then true is
	 * returned - otherwise false is returned. The given collection
	 * is a collection of error messages. If the apply was successful
	 * then the error message collection is ignored.**/
	public abstract boolean apply(Collection<String> errorMessages);
	
	/** Returns the model-view-controller model for the editor associated
	 * with this property frame. **/
	public EditorModel getModel() {
		return model;
	}
	
	/** Returns the instance associated with this property frame. **/
	public WorldObject getInstance() {
		return instance;
	}
	
	/** Adds a new JPanel with a title to the property frame. **/
	public void addPanel(String title, JPanel panel) {
		JPanel resizeablePanel = new JPanel();
		resizeablePanel.setLayout(new BorderLayout());
		JScrollPane pane = new JScrollPane(panel);
		resizeablePanel.add(pane,BorderLayout.NORTH);
		tabPane.add(resizeablePanel,title);
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	/** Shows the error messages from the given collection of error messages strings. **/
	private final void showErrorMessages(Collection<String> errorMessages) {
		if (errorMessages.isEmpty())
			return;
		
		String msg = null;
		for (String errorMessage : errorMessages) {
			msg = errorMessage;
			break;
		}
		
		JOptionPane.showMessageDialog(frame, msg, "Error", JOptionPane.ERROR_MESSAGE, null);
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** An implementation on an abstract action used for the OK button. **/
	private final class OKAction extends AbstractAction {
		
		// Auto generated serial version UID
		private static final long serialVersionUID = -4470235819143839764L;
		
		/*============================================================
		Constructors
		============================================================*/

		public OKAction() {
			putValue(AbstractAction.NAME, "OK");
		}
		
		/*============================================================
		Public Methods
		============================================================*/

		@Override public void actionPerformed(ActionEvent e) {
			ArrayDeque<String> errorMessages = new ArrayDeque<String>();
			if (apply(errorMessages))
				frame.dispose();
			else
				showErrorMessages(errorMessages);
		}
		
	}
	
	/** An implementation on an abstract action used for the Cancel button. **/
	private final class CancelAction extends AbstractAction {
		
		// Auto generated serial version UID
		private static final long serialVersionUID = 22118221220365120L;
		
		/*============================================================
		Constructors
		============================================================*/

		public CancelAction() {
			putValue(AbstractAction.NAME, "Cancel");
		}
		
		/*============================================================
		Public Methods
		============================================================*/

		@Override public void actionPerformed(ActionEvent e) {
			frame.dispose();
		}
		
	}
	
	/** An implementation on an abstract action used for the Apply button. **/
	private final class ApplyAction extends AbstractAction {
		
		// Auto generated serial version UID
		private static final long serialVersionUID = -3128678600025195287L;
		
		/*============================================================
		Constructors
		============================================================*/

		public ApplyAction() {
			putValue(AbstractAction.NAME, "Apply");
		}
		
		/*============================================================
		Public Methods
		============================================================*/

		@Override public void actionPerformed(ActionEvent arg0) {
			ArrayDeque<String> errorMessages = new ArrayDeque<String>();
			if (!apply(errorMessages))
				showErrorMessages(errorMessages);
		}
		
	}
	
	

}
